-module(xbt_tcp_proto).
-export([get_packet/1,parse_packet/1,send_packet/4]).

-include("xbt.hrl").

get_packet(Socket)  ->
    gen_tcp:recv(Socket, 0).

parse_packet(Pack)  ->
    case Pack of
        {ok,Packet}      ->
            <<Command: ?WORD, Sequence: ?WORD,Body/binary>> = Packet,
            BodyLen = byte_size(Body),
            if
                BodyLen == 0    ->
                    {ok,{Command,Sequence,[]}};
                BodyLen >  0    ->
                    case parse_body(Command,Body) of
                        {ok, Data}   ->
                            {ok,{Command,Sequence,Data}};
                        _            ->
                            {error,badpacket}
                    end
            end;
        {error,closed} ->
            {error,closed};
        {error,_Reason} ->
            %%other error will reset the connection also.
            {error,closed}
    end.

send_packet(Socket,Command,Sequence,Data) ->
    case parse_data(Command,Data) of
        {ok,Body}     ->
            Response = <<Command: ?WORD, Sequence: ?WORD, Body/binary>>,
            ok = gen_tcp:send(Socket,Response);
        _             ->
            {error,badCommandorData}
    end.


parse_body(Command,Body)           ->
    case Command of
        ?TABLE_BIND                ->
            if
                byte_size(Body) == (6+16+2+10)  ->
                    <<UserName:6/binary,Authentiator_Code:16/binary,
                      Version: ?WORD, TimeStamp: 10/binary>> = Body,
                    {ok,{UserName,Authentiator_Code,Version,TimeStamp}};
                true                       ->
                    {error,badbody}
            end;
        ?TABLE_BIND_RESP           ->
            if
                byte_size(Body) == (2+2)        ->
                    <<Status: ?WORD, Version: ?WORD>> = Body,
                    {ok,{Status,Version}};
                true                       ->
                    {error,badbody}
            end;
        ?TABLE_UNBIND              ->
            {error,badbody};
        ?TABLE_UNBIND_RESP         ->
            {error,badbody};
        ?TABLE_ACTIVE_TEST         ->
            {error,badbody};
        ?TABLE_ACTIVE_TEST_RESP    ->
            {error,badbody};
        ?TABLE_SET                 ->
            if
                byte_size(Body) > 2             ->
                    <<NameLen: ?WORD, NameValue/binary>> = Body,
                    if
                        NameLen < byte_size(NameValue)  ->
                            <<Name: NameLen/binary,Value/binary>> = NameValue,
                            {ok,{Name,Value}};
                        NameLen == byte_size(NameValue) ->
                            {ok,{NameValue,<<>>}};
                        true                        ->
                            {error,badbody}
                    end;
                true                       ->
                    {error,badbody}
            end;
        ?TABLE_SET_RESP            ->
            if
                byte_size(Body) == 2            ->
                    <<Status: ?WORD>> = Body,
                    {ok,{Status}};
                true                       ->
                    {error,badbody}
            end;
        ?TABLE_APPEND              ->
            if
                byte_size(Body) > 2             ->
                    <<NameLen: ?WORD, NameValue/binary>> = Body,
                    if
                        NameLen < byte_size(NameValue)  ->
                            <<Name: NameLen/binary,Value/binary>> = NameValue,
                            {ok,{Name,Value}};
                        NameLen == byte_size(NameValue) ->
                            {ok,{NameValue,<<>>}};
                        true                        ->
                            {error,badbody}
                    end;
                true                       ->
                    {error,badbody}
            end;
        ?TABLE_APPEND_RESP         ->
            if
                byte_size(Body) == 2            ->
                    <<Status: ?WORD>> = Body,
                    {ok,{Status}};
                true                       ->
                    {error,badbody}
            end;
        ?TABLE_DEL                 ->
            if
                byte_size(Body) > 0             ->
                    {ok,{Body}};
                true                       ->
                    {error,badbody}
            end;
        ?TABLE_DEL_RESP            ->
            if
                byte_size(Body) > 2             ->
                    <<Status: ?WORD, Name/binary>> = Body,
                    {ok,{Status,Name}};
                true                       ->
                    {error,badbody}
            end;
        ?TABLE_GET                 ->
            if
                byte_size(Body) > 0             ->
                    {ok,{Body}};
                true                       ->
                    {error,badbody}
            end;
        ?TABLE_GET_RESP            ->
            if
                byte_size(Body) > 4             ->
                    <<Status: ?WORD, NameLen: ?WORD, NameValue/binary>> = Body,
                    if
                        NameLen < byte_size(NameValue)    ->
                            <<Name: NameLen/binary,Value/binary>> = NameValue,
                            {ok,{Status,Name,Value}};
                        NameLen == byte_size(NameValue)   ->
                            {ok,{Status,NameValue}};
                        true                         ->
                            {error,badbody}
                    end;
                true                       ->
                    {error,badbody}
            end;
        _                          ->
            {error,badCommand}
    end.


parse_data(Command,Data)   ->
    case Command of
        ?TABLE_BIND                ->
            {User_Name,Authentiator_Code,Version,TimeStamp} = Data,
            Body = <<User_Name:6/binary,Authentiator_Code:16/binary,
                     Version: ?WORD, TimeStamp: 10/binary>>,
            {ok,Body};
        ?TABLE_BIND_RESP           ->
            {Status,Version} = Data,
            Body = <<Status: ?WORD, Version: ?WORD>>,
            {ok,Body};
        ?TABLE_UNBIND               ->
            {ok,<<>>};
        ?TABLE_UNBIND_RESP          ->
            {ok,<<>>};
        ?TABLE_ACTIVE_TEST         ->
            {ok,<<>>};
        ?TABLE_ACTIVE_TEST_RESP    ->
            {ok,<<>>};
        ?TABLE_SET                 ->
            {Name,Value}  = Data,
            NameLen = byte_size(Name),
            Body = <<NameLen: ?WORD, Name: NameLen/binary, Value/binary>>,
            {ok, Body};
        ?TABLE_SET_RESP            ->
            {Status} = Data,
            Body = <<Status: ?WORD>>,
            {ok, Body};
        ?TABLE_APPEND              ->
            {Name,Value} = Data,
            NameLen = byte_size(Name),
            Body = <<NameLen: ?WORD, Name: NameLen/binary, Value/binary>>,
            {ok, Body};
        ?TABLE_APPEND_RESP         ->
            {Status} = Data,
            Body = <<Status: ?WORD>>,
            {ok, Body};
        ?TABLE_DEL                 ->
            {Name} = Data,
            {ok, Name};
        ?TABLE_DEL_RESP            ->
            {Status,Name} = Data,
            Body = <<Status: ?WORD, Name/binary>>,
            {ok, Body};
        ?TABLE_GET                 ->
            {Name} = Data,
            {ok, Name};
        ?TABLE_GET_RESP            ->
            {Status,Name,Value} = Data,
            NameLen = byte_size(Name),
            Body = <<Status: ?WORD, NameLen: ?WORD, Name/binary,Value/binary>>,
            {ok, Body};
        _                          ->
            {error,badCommand}
    end.

